//
// Created on 6/4/25.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef NEURONVIEW_STREAM_H
#define NEURONVIEW_STREAM_H

//#include <istream>
#include <iostream>
#include <stack>
#include <vector>

class StreamBuf: public std::streambuf {
	template<typename T> friend class FilterInput;
	template<typename T> friend class FilterOutput;
	public:
	virtual bool close() { return true; }
};

class InputStream: public std::istream {
	std::stack<StreamBuf*> bufs;
	protected:
	public:
	InputStream(): std::istream(std::cin.rdbuf()), bufs{} { }
	InputStream(const char* fn): std::istream(std::cin.rdbuf()), bufs{} { this->open(fn); }
	InputStream(const std::string& fn): std::istream(std::cin.rdbuf()), bufs{} { this->open(fn); }
	InputStream(FILE* f): std::istream(std::cin.rdbuf()), bufs{} { this->open(f); }
	InputStream(const std::vector<char*>* bufs, size_t bufjunk): std::istream(std::cin.rdbuf()), bufs{} { this->open(bufs, bufjunk); }
	InputStream(const InputStream&) =delete;
	~InputStream() {
		while(!bufs.empty()) {
			auto v=bufs.top();
			delete v;
			bufs.pop();
		}
	}

	InputStream& operator=(const InputStream&) =delete;
	
	StreamBuf* rdbuf() const { return bufs.empty()?nullptr:bufs.top(); }

	bool is_open() const { return !bufs.empty(); }
	bool open(const char* fn);
	bool open(const std::string& fn) { return this->open(fn.c_str()); }
	bool open(FILE* f);
	bool open(const std::vector<char*>* bufs, size_t bufjunk);
	bool close() {
		if(bufs.size()==0) {
			return true;
		}
		if(bufs.size()>1) {
			this->setstate(ios_base::failbit);
			return false;
		}
		auto p=bufs.top();
		if(!p->close()) {
			this->setstate(ios_base::failbit);
			return false;
		}
		bufs.pop();
		delete p;
		this->clear();
		return true;
	}

	bool pushGzip();
	bool pop() {
		if(bufs.size()<=1) {
			fprintf(stderr, "already popped\n");
			return true;
		}
		auto p=bufs.top();
		if(!p->close()) {
			fprintf(stderr, "already close failed\n");
			this->setstate(ios_base::failbit);
			return false;
		}
		bufs.pop();
		delete p;
		this->clear();
		return true;
	}
};

class OutputStream: public std::ostream {
	std::stack<StreamBuf*> bufs;
	protected:
	public:
	OutputStream(): std::ostream(std::cout.rdbuf()), bufs{} { }
	OutputStream(const char* fn): std::ostream(std::cout.rdbuf()), bufs{} { this->open(fn); }
	OutputStream(const std::string& fn): std::ostream(std::cout.rdbuf()), bufs{} { this->open(fn); }
	OutputStream(FILE* f): std::ostream(std::cout.rdbuf()), bufs{} { this->open(f); }
	OutputStream(const OutputStream&) =delete;
	~OutputStream() {
		while(!bufs.empty()) {
			auto v=bufs.top();
			delete v;
			bufs.pop();
		}
	}

	OutputStream& operator=(const OutputStream&) =delete;

	StreamBuf* rdbuf() const { return bufs.empty()?nullptr:bufs.top(); }

	bool is_open() const { return !bufs.empty(); }
	bool open(const char* fn);
	bool open(const std::string& fn) { return this->open(fn.c_str()); }
	bool open(FILE* f);
	bool close() {
		if(bufs.size()==0) {
			return true;
		}
		if(bufs.size()>1) {
			this->setstate(ios_base::failbit);
			return false;
		}
		auto p=bufs.top();
		if(!p->close()) {
			this->setstate(ios_base::failbit);
			return false;
		}
		bufs.pop();
		delete p;
		this->clear();
		return true;
	}

	bool pushGzip();
	bool pop() {
		if(bufs.size()<=1) {
			fprintf(stderr, "already popped\n");
			return true;
		}
		auto p=bufs.top();
		if(!p->close()) {
			fprintf(stderr, "already failed tocl\n");
			this->setstate(ios_base::failbit);
			return false;
		}
		bufs.pop();
		delete p;
		this->clear();
		return true;
	}
};

#endif //NEURONVIEW_STREAM_H
